<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#proxy"  class="anchor" id="user-content-proxy">

    </a>Proxy
    </h1>
    <h2><a href="#概述"  class="anchor" id="user-content-概述">

    </a>概述
    </h2>
    <p>Proxy 用于修改某些操作的默认行为，等同于在语言层面做出修改，所以属于一种“元编程”（meta programming），即对编程语言进行编程。</p>
    <p>Proxy 可以理解成，在目标对象之前架设一层“拦截”，外界对该对象的访问，都必须先通过这层拦截，因此提供了一种机制，可以对外界的访问进行过滤和改写。Proxy
        这个词的原意是代理，用在这里表示由它来“代理”某些操作，可以译为“代理器”。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">get</span><span class="pl-k">:</span> <span class="pl-k">function</span> (<span class="pl-smi">target</span>, <span
                class="pl-smi">key</span>, <span class="pl-smi">receiver</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span>getting <span
                class="pl-s1"><span class="pl-pse">${</span>key<span class="pl-pse">}</span></span>!<span
                class="pl-pds">`</span></span>);
    <span class="pl-k">return</span> <span class="pl-c1">Reflect</span>.<span class="pl-c1">get</span>(target, key, receiver);
  },
  <span class="pl-en">set</span><span class="pl-k">:</span> <span class="pl-k">function</span> (<span class="pl-smi">target</span>, <span
                class="pl-smi">key</span>, <span class="pl-smi">value</span>, <span class="pl-smi">receiver</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span>setting <span
                class="pl-s1"><span class="pl-pse">${</span>key<span class="pl-pse">}</span></span>!<span
                class="pl-pds">`</span></span>);
    <span class="pl-k">return</span> <span class="pl-c1">Reflect</span>.<span class="pl-c1">set</span>(target, key, value, receiver);
  }
});</pre>
    </div>
    <p>
        上面代码对一个空对象架设了一层拦截，重定义了属性的读取（<code>get</code>）和设置（<code>set</code>）行为。这里暂时先不解释具体的语法，只看运行结果。对设置了拦截行为的对象<code>obj</code>，去读写它的属性，就会得到下面的结果。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">obj</span>.<span
            class="pl-smi">count</span> <span class="pl-k">=</span> <span class="pl-c1">1</span>
<span class="pl-c"><span class="pl-c">//</span>  setting count!</span>
<span class="pl-k">++</span><span class="pl-smi">obj</span>.<span class="pl-smi">count</span>
<span class="pl-c"><span class="pl-c">//</span>  getting count!</span>
<span class="pl-c"><span class="pl-c">//</span>  setting count!</span>
<span class="pl-c"><span class="pl-c">//</span>  2</span></pre>
    </div>
    <p>上面代码说明，Proxy 实际上重载（overload）了点运算符，即用自己的定义覆盖了语言的原始定义。</p>
    <p>ES6 原生提供 Proxy 构造函数，用来生成 Proxy 实例。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);</pre>
    </div>
    <p>Proxy 对象的所有用法，都是上面这种形式，不同的只是<code>handler</code>参数的写法。其中，<code>new
        Proxy()</code>表示生成一个<code>Proxy</code>实例，<code>target</code>参数表示所要拦截的目标对象，<code>handler</code>参数也是一个对象，用来定制拦截行为。
    </p>
    <p>下面是另一个拦截读取属性行为的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> proxy <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">get</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">property</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">35</span>;
  }
});

<span class="pl-smi">proxy</span>.<span class="pl-smi">time</span> <span class="pl-c"><span
                class="pl-c">//</span> 35</span>
<span class="pl-smi">proxy</span>.<span class="pl-c1">name</span> <span class="pl-c"><span
                class="pl-c">//</span> 35</span>
<span class="pl-smi">proxy</span>.<span class="pl-c1">title</span> <span class="pl-c"><span
                class="pl-c">//</span> 35</span></pre>
    </div>
    <p>上面代码中，作为构造函数，<code>Proxy</code>接受两个参数。第一个参数是所要代理的目标对象（上例是一个空对象），即如果没有<code>Proxy</code>的介入，操作原来要访问的就是这个对象；第二个参数是一个配置对象，对于每一个被代理的操作，需要提供一个对应的处理函数，该函数将拦截对应的操作。比如，上面代码中，配置对象有一个<code>get</code>方法，用来拦截对目标对象属性的访问请求。<code>get</code>方法的两个参数分别是目标对象和所要访问的属性。可以看到，由于拦截函数总是返回<code>35</code>，所以访问任何属性都得到<code>35</code>。
    </p>
    <p>注意，要使得<code>Proxy</code>起作用，必须针对<code>Proxy</code>实例（上例是<code>proxy</code>对象）进行操作，而不是针对目标对象（上例是空对象）进行操作。</p>
    <p>如果<code>handler</code>没有设置任何拦截，那就等同于直接通向原对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> target <span class="pl-k">=</span> {};
<span class="pl-k">var</span> handler <span class="pl-k">=</span> {};
<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);
<span class="pl-smi">proxy</span>.<span class="pl-smi">a</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>;
<span class="pl-smi">target</span>.<span class="pl-smi">a</span> <span class="pl-c"><span
                class="pl-c">//</span> "b"</span></pre>
    </div>
    <p>上面代码中，<code>handler</code>是一个空对象，没有任何拦截效果，访问<code>proxy</code>就等同于访问<code>target</code>。</p>
    <p>一个技巧是将 Proxy 对象，设置到<code>object.proxy</code>属性，从而可以在<code>object</code>对象上调用。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">var</span> object <span class="pl-k">=</span> { proxy<span class="pl-k">:</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>(target, handler) };</pre>
    </div>
    <p>Proxy 实例也可以作为其他对象的原型对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> proxy <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">get</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">property</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">35</span>;
  }
});

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(proxy);
<span class="pl-smi">obj</span>.<span class="pl-smi">time</span> <span class="pl-c"><span
                class="pl-c">//</span> 35</span></pre>
    </div>
    <p>
        上面代码中，<code>proxy</code>对象是<code>obj</code>对象的原型，<code>obj</code>对象本身并没有<code>time</code>属性，所以根据原型链，会在<code>proxy</code>对象上读取该属性，导致被拦截。
    </p>
    <p>同一个拦截器函数，可以设置拦截多个操作。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">get</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">name</span>) {
    <span class="pl-k">if</span> (name <span class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">'</span>prototype<span
                class="pl-pds">'</span></span>) {
      <span class="pl-k">return</span> <span class="pl-c1">Object</span>.<span class="pl-c1">prototype</span>;
    }
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>Hello, <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> name;
  },

  <span class="pl-en">apply</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">thisBinding</span>, <span class="pl-smi">args</span>) {
    <span class="pl-k">return</span> args[<span class="pl-c1">0</span>];
  },

  <span class="pl-en">construct</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">target</span>, <span class="pl-smi">args</span>) {
    <span class="pl-k">return</span> {value<span class="pl-k">:</span> args[<span class="pl-c1">1</span>]};
  }
};

<span class="pl-k">var</span> fproxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(<span class="pl-k">function</span>(<span class="pl-smi">x</span>, <span
                class="pl-smi">y</span>) {
  <span class="pl-k">return</span> x <span class="pl-k">+</span> y;
}, handler);

<span class="pl-en">fproxy</span>(<span class="pl-c1">1</span>, <span class="pl-c1">2</span>) <span class="pl-c"><span
                class="pl-c">//</span> 1</span>
<span class="pl-k">new</span> <span class="pl-en">fproxy</span>(<span class="pl-c1">1</span>, <span
                class="pl-c1">2</span>) <span class="pl-c"><span class="pl-c">//</span> {value: 2}</span>
<span class="pl-smi">fproxy</span>.<span class="pl-c1">prototype</span> <span class="pl-k">===</span> <span
                class="pl-c1">Object</span>.<span class="pl-c1">prototype</span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-smi">fproxy</span>.<span class="pl-smi">foo</span> <span class="pl-k">===</span> <span
                class="pl-s"><span class="pl-pds">"</span>Hello, foo<span class="pl-pds">"</span></span> <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>对于可以设置、但没有设置拦截的操作，则直接落在目标对象上，按照原先的方式产生结果。</p>
    <p>下面是 Proxy 支持的拦截操作一览，一共 13 种。</p>
    <ul>
        <li><strong>get(target, propKey, receiver)</strong>：拦截对象属性的读取，比如<code>proxy.foo</code>和<code>proxy['foo']</code>。
        </li>
        <li><strong>set(target, propKey, value, receiver)</strong>：拦截对象属性的设置，比如<code>proxy.foo = v</code>或<code>proxy['foo']
            = v</code>，返回一个布尔值。
        </li>
        <li><strong>has(target, propKey)</strong>：拦截<code>propKey in proxy</code>的操作，返回一个布尔值。</li>
        <li><strong>deleteProperty(target, propKey)</strong>：拦截<code>delete proxy[propKey]</code>的操作，返回一个布尔值。</li>
        <li><strong>ownKeys(target)</strong>：拦截<code>Object.getOwnPropertyNames(proxy)</code>、<code>Object.getOwnPropertySymbols(proxy)</code>、<code>Object.keys(proxy)</code>，返回一个数组。该方法返回目标对象所有自身的属性的属性名，而<code>Object.keys()</code>的返回结果仅包括目标对象自身的可遍历属性。
        </li>
        <li><strong>getOwnPropertyDescriptor(target, propKey)</strong>：拦截<code>Object.getOwnPropertyDescriptor(proxy,
            propKey)</code>，返回属性的描述对象。
        </li>
        <li><strong>defineProperty(target, propKey, propDesc)</strong>：拦截<code>Object.defineProperty(proxy, propKey,
            propDesc）</code>、<code>Object.defineProperties(proxy, propDescs)</code>，返回一个布尔值。
        </li>
        <li><strong>preventExtensions(target)</strong>：拦截<code>Object.preventExtensions(proxy)</code>，返回一个布尔值。</li>
        <li><strong>getPrototypeOf(target)</strong>：拦截<code>Object.getPrototypeOf(proxy)</code>，返回一个对象。</li>
        <li><strong>isExtensible(target)</strong>：拦截<code>Object.isExtensible(proxy)</code>，返回一个布尔值。</li>
        <li><strong>setPrototypeOf(target, proto)</strong>：拦截<code>Object.setPrototypeOf(proxy, proto)</code>，返回一个布尔值。如果目标对象是函数，那么还有两种额外操作可以拦截。
        </li>
        <li><strong>apply(target, object, args)</strong>：拦截 Proxy 实例作为函数调用的操作，比如<code>proxy(...args)</code>、<code>proxy.call(object,
            ...args)</code>、<code>proxy.apply(...)</code>。
        </li>
        <li><strong>construct(target, args)</strong>：拦截 Proxy 实例作为构造函数调用的操作，比如<code>new proxy(...args)</code>。</li>
    </ul>
    <h2><a href="#proxy-实例的方法"  class="anchor" id="user-content-proxy-实例的方法">

    </a>Proxy 实例的方法
    </h2>
    <p>下面是上面这些拦截方法的详细介绍。</p>
    <h3><a href="#get"  class="anchor" id="user-content-get">

    </a>get()
    </h3>
    <p><code>get</code>方法用于拦截某个属性的读取操作，可以接受三个参数，依次为目标对象、属性名和 proxy 实例本身（即<code>this</code>关键字指向的那个对象），其中最后一个参数可选。</p>
    <p><code>get</code>方法的用法，上文已经有一个例子，下面是另一个拦截读取操作的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> person <span class="pl-k">=</span> {
  name<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>张三<span class="pl-pds">"</span></span>
};

<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(person, {
  <span class="pl-en">get</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">property</span>) {
    <span class="pl-k">if</span> (property <span class="pl-k">in</span> target) {
      <span class="pl-k">return</span> target[property];
    } <span class="pl-k">else</span> {
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">ReferenceError</span>(<span
                class="pl-s"><span class="pl-pds">"</span>Property <span class="pl-cce">\"</span><span
                class="pl-pds">"</span></span> <span class="pl-k">+</span> property <span class="pl-k">+</span> <span
                class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\"</span> does not exist.<span
                class="pl-pds">"</span></span>);
    }
  }
});

<span class="pl-smi">proxy</span>.<span class="pl-c1">name</span> <span class="pl-c"><span
                class="pl-c">//</span> "张三"</span>
<span class="pl-smi">proxy</span>.<span class="pl-smi">age</span> <span class="pl-c"><span class="pl-c">//</span> 抛出一个错误</span></pre>
    </div>
    <p>上面代码表示，如果访问目标对象不存在的属性，会抛出一个错误。如果没有这个拦截函数，访问不存在的属性，只会返回<code>undefined</code>。</p>
    <p><code>get</code>方法可以继承。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> proto <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">get</span>(<span class="pl-smi">target</span>, <span class="pl-smi">propertyKey</span>, <span
                class="pl-smi">receiver</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>GET <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> propertyKey);
    <span class="pl-k">return</span> target[propertyKey];
  }
});

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(proto);
<span class="pl-smi">obj</span>.<span class="pl-smi">foo</span> <span class="pl-c"><span class="pl-c">//</span> "GET foo"</span></pre>
    </div>
    <p>上面代码中，拦截操作定义在<code>Prototype</code>对象上面，所以如果读取<code>obj</code>对象继承的属性时，拦截会生效。</p>
    <p>下面的例子使用<code>get</code>拦截，实现数组读取负数的索引。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">createArray</span>(<span class="pl-k">...</span><span class="pl-v">elements</span>) {
  <span class="pl-k">let</span> handler <span class="pl-k">=</span> {
    <span class="pl-en">get</span>(<span class="pl-smi">target</span>, <span class="pl-smi">propKey</span>, <span
                class="pl-smi">receiver</span>) {
      <span class="pl-k">let</span> index <span class="pl-k">=</span> <span class="pl-c1">Number</span>(propKey);
      <span class="pl-k">if</span> (index <span class="pl-k">&lt;</span> <span class="pl-c1">0</span>) {
        propKey <span class="pl-k">=</span> <span class="pl-c1">String</span>(<span class="pl-smi">target</span>.<span
                class="pl-c1">length</span> <span class="pl-k">+</span> index);
      }
      <span class="pl-k">return</span> <span class="pl-c1">Reflect</span>.<span class="pl-c1">get</span>(target, propKey, receiver);
    }
  };

  <span class="pl-k">let</span> target <span class="pl-k">=</span> [];
  <span class="pl-smi">target</span>.<span class="pl-c1">push</span>(<span class="pl-k">...</span>elements);
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>(target, handler);
}

<span class="pl-k">let</span> arr <span class="pl-k">=</span> <span class="pl-en">createArray</span>(<span class="pl-s"><span
                class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>c<span class="pl-pds">'</span></span>);
arr[<span class="pl-k">-</span><span class="pl-c1">1</span>] <span class="pl-c"><span
                class="pl-c">//</span> c</span></pre>
    </div>
    <p>上面代码中，数组的位置参数是<code>-1</code>，就会输出数组的倒数最后一个成员。</p>
    <p>利用 Proxy，可以将读取属性的操作（<code>get</code>），转变为执行某个函数，从而实现属性的链式操作。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> pipe <span
            class="pl-k">=</span> (<span class="pl-k">function</span> () {
  <span class="pl-k">return</span> <span class="pl-k">function</span> (<span class="pl-smi">value</span>) {
    <span class="pl-k">var</span> funcStack <span class="pl-k">=</span> [];
    <span class="pl-k">var</span> oproxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>({} , {
      <span class="pl-en">get</span> <span class="pl-k">:</span> <span class="pl-k">function</span> (<span
                class="pl-smi">pipeObject</span>, <span class="pl-smi">fnName</span>) {
        <span class="pl-k">if</span> (fnName <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span>get<span class="pl-pds">'</span></span>) {
          <span class="pl-k">return</span> <span class="pl-smi">funcStack</span>.<span class="pl-en">reduce</span>(<span
                class="pl-k">function</span> (<span class="pl-smi">val</span>, <span class="pl-smi">fn</span>) {
            <span class="pl-k">return</span> <span class="pl-en">fn</span>(val);
          },value);
        }
        <span class="pl-smi">funcStack</span>.<span class="pl-c1">push</span>(<span class="pl-c1">window</span>[fnName]);
        <span class="pl-k">return</span> oproxy;
      }
    });

    <span class="pl-k">return</span> oproxy;
  }
}());

<span class="pl-k">var</span> <span class="pl-en">double</span> <span class="pl-k">=</span> <span
                class="pl-smi">n</span> <span class="pl-k">=&gt;</span> n <span class="pl-k">*</span> <span
                class="pl-c1">2</span>;
<span class="pl-k">var</span> <span class="pl-en">pow</span>    <span class="pl-k">=</span> <span
                class="pl-smi">n</span> <span class="pl-k">=&gt;</span> n <span class="pl-k">*</span> n;
<span class="pl-k">var</span> <span class="pl-en">reverseInt</span> <span class="pl-k">=</span> <span
                class="pl-smi">n</span> <span class="pl-k">=&gt;</span> <span class="pl-smi">n</span>.<span
                class="pl-c1">toString</span>().<span class="pl-c1">split</span>(<span class="pl-s"><span
                class="pl-pds">"</span><span class="pl-pds">"</span></span>).<span class="pl-c1">reverse</span>().<span
                class="pl-c1">join</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>) <span
                class="pl-k">|</span> <span class="pl-c1">0</span>;

<span class="pl-en">pipe</span>(<span class="pl-c1">3</span>).<span class="pl-smi">double</span>.<span class="pl-smi">pow</span>.<span
                class="pl-smi">reverseInt</span>.<span class="pl-smi">get</span>; <span class="pl-c"><span class="pl-c">//</span> 63</span></pre>
    </div>
    <p>上面代码设置 Proxy 以后，达到了将函数名链式使用的效果。</p>
    <p>下面的例子则是利用<code>get</code>拦截，实现一个生成各种 DOM 节点的通用函数<code>dom</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">dom</span> <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">get</span>(<span class="pl-smi">target</span>, <span class="pl-smi">property</span>) {
    <span class="pl-k">return</span> <span class="pl-k">function</span>(<span class="pl-smi">attrs</span> <span
                class="pl-k">=</span> {}, <span class="pl-k">...</span><span class="pl-v">children</span>) {
      <span class="pl-k">const</span> <span class="pl-c1">el</span> <span class="pl-k">=</span> <span class="pl-c1">document</span>.<span
                class="pl-c1">createElement</span>(property);
      <span class="pl-k">for</span> (<span class="pl-k">let</span> prop <span class="pl-k">of</span> <span
                class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(attrs)) {
        <span class="pl-smi">el</span>.<span class="pl-c1">setAttribute</span>(prop, attrs[prop]);
      }
      <span class="pl-k">for</span> (<span class="pl-k">let</span> child <span class="pl-k">of</span> children) {
        <span class="pl-k">if</span> (<span class="pl-k">typeof</span> child <span class="pl-k">===</span> <span
                class="pl-s"><span class="pl-pds">'</span>string<span class="pl-pds">'</span></span>) {
          child <span class="pl-k">=</span> <span class="pl-c1">document</span>.<span
                class="pl-c1">createTextNode</span>(child);
        }
        <span class="pl-smi">el</span>.<span class="pl-c1">appendChild</span>(child);
      }
      <span class="pl-k">return</span> el;
    }
  }
});

<span class="pl-k">const</span> <span class="pl-c1">el</span> <span class="pl-k">=</span> <span
                class="pl-smi">dom</span>.<span class="pl-en">div</span>({},
  <span class="pl-s"><span class="pl-pds">'</span>Hello, my name is <span class="pl-pds">'</span></span>,
  <span class="pl-smi">dom</span>.<span class="pl-en">a</span>({href<span class="pl-k">:</span> <span class="pl-s"><span
                class="pl-pds">'</span>//example.com<span class="pl-pds">'</span></span>}, <span class="pl-s"><span
                class="pl-pds">'</span>Mark<span class="pl-pds">'</span></span>),
  <span class="pl-s"><span class="pl-pds">'</span>. I like:<span class="pl-pds">'</span></span>,
  <span class="pl-smi">dom</span>.<span class="pl-en">ul</span>({},
    <span class="pl-smi">dom</span>.<span class="pl-en">li</span>({}, <span class="pl-s"><span class="pl-pds">'</span>The web<span
                class="pl-pds">'</span></span>),
    <span class="pl-smi">dom</span>.<span class="pl-en">li</span>({}, <span class="pl-s"><span class="pl-pds">'</span>Food<span
                class="pl-pds">'</span></span>),
    <span class="pl-smi">dom</span>.<span class="pl-en">li</span>({}, <span class="pl-s"><span class="pl-pds">'</span>…actually that<span
                class="pl-cce">\'</span>s it<span class="pl-pds">'</span></span>)
  )
);

<span class="pl-c1">document</span>.<span class="pl-c1">body</span>.<span class="pl-c1">appendChild</span>(el);</pre>
    </div>
    <p>下面是一个<code>get</code>方法的第三个参数的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">proxy</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">get</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">property</span>, <span class="pl-smi">receiver</span>) {
    <span class="pl-k">return</span> receiver;
  }
});
<span class="pl-smi">proxy</span>.<span class="pl-smi">getReceiver</span> <span class="pl-k">===</span> proxy <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，<code>get</code>方法的第三个参数<code>receiver</code>，总是为当前的 Proxy 实例。</p>
    <p>如果一个属性不可配置（configurable）和不可写（writable），则该属性不能被代理，通过 Proxy 对象访问该属性会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">target</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span
            class="pl-en">defineProperties</span>({}, {
  foo<span class="pl-k">:</span> {
    value<span class="pl-k">:</span> <span class="pl-c1">123</span>,
    writable<span class="pl-k">:</span> <span class="pl-c1">false</span>,
    configurable<span class="pl-k">:</span> <span class="pl-c1">false</span>
  },
});

<span class="pl-k">const</span> <span class="pl-c1">handler</span> <span class="pl-k">=</span> {
  <span class="pl-en">get</span>(<span class="pl-smi">target</span>, <span class="pl-smi">propKey</span>) {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span
                class="pl-pds">'</span></span>;
  }
};

<span class="pl-k">const</span> <span class="pl-c1">proxy</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>(target, handler);

<span class="pl-smi">proxy</span>.<span class="pl-smi">foo</span>
<span class="pl-c"><span class="pl-c">//</span> TypeError: Invariant check failed</span></pre>
    </div>
    <h3><a href="#set"  class="anchor" id="user-content-set">

    </a>set()
    </h3>
    <p><code>set</code>方法用来拦截某个属性的赋值操作，可以接受四个参数，依次为目标对象、属性名、属性值和 Proxy 实例本身，其中最后一个参数可选。</p>
    <p>假定<code>Person</code>对象有一个<code>age</code>属性，该属性应该是一个不大于 200 的整数，那么可以使用<code>Proxy</code>保证<code>age</code>的属性值符合要求。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> validator <span class="pl-k">=</span> {
  <span class="pl-en">set</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">obj</span>, <span class="pl-smi">prop</span>, <span class="pl-smi">value</span>) {
    <span class="pl-k">if</span> (prop <span class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">'</span>age<span
                class="pl-pds">'</span></span>) {
      <span class="pl-k">if</span> (<span class="pl-k">!</span><span class="pl-c1">Number</span>.<span class="pl-en">isInteger</span>(value)) {
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">TypeError</span>(<span
                class="pl-s"><span class="pl-pds">'</span>The age is not an integer<span class="pl-pds">'</span></span>);
      }
      <span class="pl-k">if</span> (value <span class="pl-k">&gt;</span> <span class="pl-c1">200</span>) {
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">RangeError</span>(<span
                class="pl-s"><span class="pl-pds">'</span>The age seems invalid<span class="pl-pds">'</span></span>);
      }
    }

    <span class="pl-c"><span class="pl-c">//</span> 对于age以外的属性，直接保存</span>
    obj[prop] <span class="pl-k">=</span> value;
  }
};

<span class="pl-k">let</span> person <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>({}, validator);

<span class="pl-smi">person</span>.<span class="pl-smi">age</span> <span class="pl-k">=</span> <span
                class="pl-c1">100</span>;

<span class="pl-smi">person</span>.<span class="pl-smi">age</span> <span class="pl-c"><span
                class="pl-c">//</span> 100</span>
<span class="pl-smi">person</span>.<span class="pl-smi">age</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>young<span class="pl-pds">'</span></span> <span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-smi">person</span>.<span class="pl-smi">age</span> <span class="pl-k">=</span> <span
                class="pl-c1">300</span> <span class="pl-c"><span class="pl-c">//</span> 报错</span></pre>
    </div>
    <p>上面代码中，由于设置了存值函数<code>set</code>，任何不符合要求的<code>age</code>属性赋值，都会抛出一个错误，这是数据验证的一种实现方法。利用<code>set</code>方法，还可以数据绑定，即每当对象发生变化时，会自动更新
        DOM。</p>
    <p>有时，我们会在对象上面设置内部属性，属性名的第一个字符使用下划线开头，表示这些属性不应该被外部使用。结合<code>get</code>和<code>set</code>方法，就可以做到防止这些内部属性被外部读写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">handler</span> <span class="pl-k">=</span> {
  <span class="pl-en">get</span> (target, key) {
    <span class="pl-en">invariant</span>(key, <span class="pl-s"><span class="pl-pds">'</span>get<span
                class="pl-pds">'</span></span>);
    <span class="pl-k">return</span> target[key];
  },
  <span class="pl-en">set</span> (target, key, value) {
    <span class="pl-en">invariant</span>(key, <span class="pl-s"><span class="pl-pds">'</span>set<span
                class="pl-pds">'</span></span>);
    target[key] <span class="pl-k">=</span> value;
    <span class="pl-k">return</span> <span class="pl-c1">true</span>;
  }
};
<span class="pl-k">function</span> <span class="pl-en">invariant</span> (<span class="pl-smi">key</span>, <span
                class="pl-smi">action</span>) {
  <span class="pl-k">if</span> (key[<span class="pl-c1">0</span>] <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span>_<span class="pl-pds">'</span></span>) {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">`</span>Invalid attempt to <span class="pl-s1"><span
                class="pl-pse">${</span>action<span class="pl-pse">}</span></span> private "<span class="pl-s1"><span
                class="pl-pse">${</span>key<span class="pl-pse">}</span></span>" property<span
                class="pl-pds">`</span></span>);
  }
}
<span class="pl-k">const</span> <span class="pl-c1">target</span> <span class="pl-k">=</span> {};
<span class="pl-k">const</span> <span class="pl-c1">proxy</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>(target, handler);
<span class="pl-smi">proxy</span>.<span class="pl-smi">_prop</span>
<span class="pl-c"><span class="pl-c">//</span> Error: Invalid attempt to get private "_prop" property</span>
<span class="pl-smi">proxy</span>.<span class="pl-smi">_prop</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>c<span class="pl-pds">'</span></span>
<span class="pl-c"><span class="pl-c">//</span> Error: Invalid attempt to set private "_prop" property</span></pre>
    </div>
    <p>上面代码中，只要读写的属性名的第一个字符是下划线，一律抛错，从而达到禁止读写内部属性的目的。</p>
    <p>下面是<code>set</code>方法第四个参数的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">handler</span> <span class="pl-k">=</span> {
  <span class="pl-en">set</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">obj</span>, <span class="pl-smi">prop</span>, <span class="pl-smi">value</span>, <span
                class="pl-smi">receiver</span>) {
    obj[prop] <span class="pl-k">=</span> receiver;
  }
};
<span class="pl-k">const</span> <span class="pl-c1">proxy</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, handler);
<span class="pl-smi">proxy</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>;
<span class="pl-smi">proxy</span>.<span class="pl-smi">foo</span> <span class="pl-k">===</span> proxy <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，<code>set</code>方法的第四个参数<code>receiver</code>，总是返回<code>this</code>关键字所指向的那个对象，即<code>proxy</code>实例本身。</p>
    <p>注意，如果目标对象自身的某个属性，不可写也不可配置，那么<code>set</code>不得改变这个属性的值，只能返回同样的值，否则报错。</p>
    <h3><a href="#apply"  class="anchor" id="user-content-apply">

    </a>apply()
    </h3>
    <p><code>apply</code>方法拦截函数的调用、<code>call</code>和<code>apply</code>操作。</p>
    <p><code>apply</code>方法可以接受三个参数，分别是目标对象、目标对象的上下文对象（<code>this</code>）和目标对象的参数数组。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">apply</span> (<span class="pl-smi">target</span>, <span class="pl-smi">ctx</span>, <span
                class="pl-smi">args</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">Reflect</span>.<span class="pl-c1">apply</span>(<span
                class="pl-k">...</span><span class="pl-c1">arguments</span>);
  }
};</pre>
    </div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> <span
            class="pl-en">target</span> <span class="pl-k">=</span> <span class="pl-k">function</span> () { <span
            class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>I am the target<span
            class="pl-pds">'</span></span>; };
<span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">apply</span><span class="pl-k">:</span> <span class="pl-k">function</span> () {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>I am the proxy<span class="pl-pds">'</span></span>;
  }
};

<span class="pl-k">var</span> p <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);

<span class="pl-en">p</span>()
<span class="pl-c"><span class="pl-c">//</span> "I am the proxy"</span></pre>
    </div>
    <p>上面代码中，变量<code>p</code>是 Proxy 的实例，当它作为函数调用时（<code>p()</code>），就会被<code>apply</code>方法拦截，返回一个字符串。</p>
    <p>下面是另外一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> twice <span class="pl-k">=</span> {
  <span class="pl-en">apply</span> (<span class="pl-smi">target</span>, <span class="pl-smi">ctx</span>, <span
                class="pl-smi">args</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">Reflect</span>.<span class="pl-c1">apply</span>(<span
                class="pl-k">...</span><span class="pl-c1">arguments</span>) <span class="pl-k">*</span> <span
                class="pl-c1">2</span>;
  }
};
<span class="pl-k">function</span> <span class="pl-en">sum</span> (<span class="pl-smi">left</span>, <span
                class="pl-smi">right</span>) {
  <span class="pl-k">return</span> left <span class="pl-k">+</span> right;
};
<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(sum, twice);
<span class="pl-en">proxy</span>(<span class="pl-c1">1</span>, <span class="pl-c1">2</span>) <span class="pl-c"><span
                class="pl-c">//</span> 6</span>
<span class="pl-smi">proxy</span>.<span class="pl-c1">call</span>(<span class="pl-c1">null</span>, <span
                class="pl-c1">5</span>, <span class="pl-c1">6</span>) <span class="pl-c"><span class="pl-c">//</span> 22</span>
<span class="pl-smi">proxy</span>.<span class="pl-c1">apply</span>(<span class="pl-c1">null</span>, [<span
                class="pl-c1">7</span>, <span class="pl-c1">8</span>]) <span class="pl-c"><span class="pl-c">//</span> 30</span></pre>
    </div>
    <p>上面代码中，每当执行<code>proxy</code>函数（直接调用或<code>call</code>和<code>apply</code>调用），就会被<code>apply</code>方法拦截。</p>
    <p>另外，直接调用<code>Reflect.apply</code>方法，也会被拦截。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-c1">Reflect</span>.<span class="pl-c1">apply</span>(proxy, <span class="pl-c1">null</span>, [<span
                class="pl-c1">9</span>, <span class="pl-c1">10</span>]) <span class="pl-c"><span class="pl-c">//</span> 38</span></pre>
    </div>
    <h3><a href="#has"  class="anchor" id="user-content-has">

    </a>has()
    </h3>
    <p><code>has</code>方法用来拦截<code>HasProperty</code>操作，即判断对象是否具有某个属性时，这个方法会生效。典型的操作就是<code>in</code>运算符。</p>
    <p>下面的例子使用<code>has</code>方法隐藏某些属性，不被<code>in</code>运算符发现。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">has</span> (<span class="pl-smi">target</span>, <span class="pl-smi">key</span>) {
    <span class="pl-k">if</span> (key[<span class="pl-c1">0</span>] <span class="pl-k">===</span> <span
                class="pl-s"><span class="pl-pds">'</span>_<span class="pl-pds">'</span></span>) {
      <span class="pl-k">return</span> <span class="pl-c1">false</span>;
    }
    <span class="pl-k">return</span> key <span class="pl-k">in</span> target;
  }
};
<span class="pl-k">var</span> target <span class="pl-k">=</span> { _prop<span class="pl-k">:</span> <span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>, prop<span
                class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span> };
<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);
<span class="pl-s"><span class="pl-pds">'</span>_prop<span class="pl-pds">'</span></span> <span class="pl-k">in</span> proxy <span
                class="pl-c"><span class="pl-c">//</span> false</span></pre>
    </div>
    <p>上面代码中，如果原对象的属性名的第一个字符是下划线，<code>proxy.has</code>就会返回<code>false</code>，从而不会被<code>in</code>运算符发现。</p>
    <p>如果原对象不可配置或者禁止扩展，这时<code>has</code>拦截会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span
            class="pl-k">=</span> { a<span class="pl-k">:</span> <span class="pl-c1">10</span> };
<span class="pl-c1">Object</span>.<span class="pl-en">preventExtensions</span>(obj);

<span class="pl-k">var</span> p <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(obj, {
  <span class="pl-en">has</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">prop</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">false</span>;
  }
});

<span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span> <span
                class="pl-k">in</span> p <span class="pl-c"><span
                class="pl-c">//</span> TypeError is thrown</span></pre>
    </div>
    <p>上面代码中，<code>obj</code>对象禁止扩展，结果使用<code>has</code>拦截就会报错。也就是说，如果某个属性不可配置（或者目标对象不可扩展），则<code>has</code>方法就不得“隐藏”（即返回<code>false</code>）目标对象的该属性。
    </p>
    <p>值得注意的是，<code>has</code>方法拦截的是<code>HasProperty</code>操作，而不是<code>HasOwnProperty</code>操作，即<code>has</code>方法不判断一个属性是对象自身的属性，还是继承的属性。
    </p>
    <p>另外，虽然<code>for...in</code>循环也用到了<code>in</code>运算符，但是<code>has</code>拦截对<code>for...in</code>循环不生效。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> stu1 <span class="pl-k">=</span> {name<span
            class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>张三<span class="pl-pds">'</span></span>, score<span
            class="pl-k">:</span> <span class="pl-c1">59</span>};
<span class="pl-k">let</span> stu2 <span class="pl-k">=</span> {name<span class="pl-k">:</span> <span class="pl-s"><span
                class="pl-pds">'</span>李四<span class="pl-pds">'</span></span>, score<span class="pl-k">:</span> <span
                class="pl-c1">99</span>};

<span class="pl-k">let</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">has</span>(<span class="pl-smi">target</span>, <span class="pl-smi">prop</span>) {
    <span class="pl-k">if</span> (prop <span class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">'</span>score<span
                class="pl-pds">'</span></span> <span class="pl-k">&amp;&amp;</span> target[prop] <span
                class="pl-k">&lt;</span> <span class="pl-c1">60</span>) {
      <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span><span
                class="pl-s1"><span class="pl-pse">${</span><span class="pl-smi">target</span>.<span
                class="pl-c1">name</span><span class="pl-pse">}</span></span> 不及格<span class="pl-pds">`</span></span>);
      <span class="pl-k">return</span> <span class="pl-c1">false</span>;
    }
    <span class="pl-k">return</span> prop <span class="pl-k">in</span> target;
  }
}

<span class="pl-k">let</span> oproxy1 <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>(stu1, handler);
<span class="pl-k">let</span> oproxy2 <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>(stu2, handler);

<span class="pl-s"><span class="pl-pds">'</span>score<span class="pl-pds">'</span></span> <span class="pl-k">in</span> oproxy1
<span class="pl-c"><span class="pl-c">//</span> 张三 不及格</span>
<span class="pl-c"><span class="pl-c">//</span> false</span>

<span class="pl-s"><span class="pl-pds">'</span>score<span class="pl-pds">'</span></span> <span class="pl-k">in</span> oproxy2
<span class="pl-c"><span class="pl-c">//</span> true</span>

<span class="pl-k">for</span> (<span class="pl-k">let</span> a <span class="pl-k">in</span> oproxy1) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(oproxy1[a]);
}
<span class="pl-c"><span class="pl-c">//</span> 张三</span>
<span class="pl-c"><span class="pl-c">//</span> 59</span>

<span class="pl-k">for</span> (<span class="pl-k">let</span> b <span class="pl-k">in</span> oproxy2) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(oproxy2[b]);
}
<span class="pl-c"><span class="pl-c">//</span> 李四</span>
<span class="pl-c"><span class="pl-c">//</span> 99</span></pre>
    </div>
    <p>上面代码中，<code>has</code>拦截只对<code>in</code>循环生效，对<code>for...in</code>循环不生效，导致不符合要求的属性没有被排除在<code>for...in</code>循环之外。
    </p>
    <h3><a href="#construct"  class="anchor" id="user-content-construct">

    </a>construct()
    </h3>
    <p><code>construct</code>方法用于拦截<code>new</code>命令，下面是拦截对象的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">construct</span> (<span class="pl-smi">target</span>, <span class="pl-smi">args</span>, <span
                class="pl-smi">newTarget</span>) {
    <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">target</span>(<span class="pl-k">...</span>args);
  }
};</pre>
    </div>
    <p><code>construct</code>方法可以接受两个参数。</p>
    <ul>
        <li><code>target</code>: 目标对象</li>
        <li><code>args</code>：构建函数的参数对象</li>
    </ul>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>(<span class="pl-k">function</span> () {}, {
  <span class="pl-en">construct</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">target</span>, <span class="pl-smi">args</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>called: <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-smi">args</span>.<span
                class="pl-c1">join</span>(<span class="pl-s"><span class="pl-pds">'</span>, <span
                class="pl-pds">'</span></span>));
    <span class="pl-k">return</span> { value<span class="pl-k">:</span> args[<span class="pl-c1">0</span>] <span
                class="pl-k">*</span> <span class="pl-c1">10</span> };
  }
});

(<span class="pl-k">new</span> <span class="pl-en">p</span>(<span class="pl-c1">1</span>)).<span
                class="pl-c1">value</span>
<span class="pl-c"><span class="pl-c">//</span> "called: 1"</span>
<span class="pl-c"><span class="pl-c">//</span> 10</span></pre>
    </div>
    <p><code>construct</code>方法返回的必须是一个对象，否则会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>(<span class="pl-k">function</span>() {}, {
  <span class="pl-en">construct</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">target</span>, <span class="pl-smi">argumentsList</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">1</span>;
  }
});

<span class="pl-k">new</span> <span class="pl-en">p</span>() <span class="pl-c"><span
                class="pl-c">//</span> 报错</span></pre>
    </div>
    <h3><a href="#deleteproperty"  class="anchor" id="user-content-deleteproperty">

    </a>deleteProperty()
    </h3>
    <p>
        <code>deleteProperty</code>方法用于拦截<code>delete</code>操作，如果这个方法抛出错误或者返回<code>false</code>，当前属性就无法被<code>delete</code>命令删除。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">deleteProperty</span> (<span class="pl-smi">target</span>, <span class="pl-smi">key</span>) {
    <span class="pl-en">invariant</span>(key, <span class="pl-s"><span class="pl-pds">'</span>delete<span
                class="pl-pds">'</span></span>);
    <span class="pl-k">return</span> <span class="pl-c1">true</span>;
  }
};
<span class="pl-k">function</span> <span class="pl-en">invariant</span> (<span class="pl-smi">key</span>, <span
                class="pl-smi">action</span>) {
  <span class="pl-k">if</span> (key[<span class="pl-c1">0</span>] <span class="pl-k">===</span> <span class="pl-s"><span
                class="pl-pds">'</span>_<span class="pl-pds">'</span></span>) {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">`</span>Invalid attempt to <span class="pl-s1"><span
                class="pl-pse">${</span>action<span class="pl-pse">}</span></span> private "<span class="pl-s1"><span
                class="pl-pse">${</span>key<span class="pl-pse">}</span></span>" property<span
                class="pl-pds">`</span></span>);
  }
}

<span class="pl-k">var</span> target <span class="pl-k">=</span> { _prop<span class="pl-k">:</span> <span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span> };
<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);
<span class="pl-k">delete</span> <span class="pl-smi">proxy</span>.<span class="pl-smi">_prop</span>
<span class="pl-c"><span class="pl-c">//</span> Error: Invalid attempt to delete private "_prop" property</span></pre>
    </div>
    <p>上面代码中，<code>deleteProperty</code>方法拦截了<code>delete</code>操作符，删除第一个字符为下划线的属性会报错。</p>
    <p>注意，目标对象自身的不可配置（configurable）的属性，不能被<code>deleteProperty</code>方法删除，否则报错。</p>
    <h3><a href="#defineproperty"  class="anchor" id="user-content-defineproperty">

    </a>defineProperty()
    </h3>
    <p><code>defineProperty</code>方法拦截了<code>Object.defineProperty</code>操作。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">defineProperty</span> (<span class="pl-smi">target</span>, <span class="pl-smi">key</span>, <span
                class="pl-smi">descriptor</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">false</span>;
  }
};
<span class="pl-k">var</span> target <span class="pl-k">=</span> {};
<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);
<span class="pl-smi">proxy</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>
<span class="pl-c"><span class="pl-c">//</span> TypeError: proxy defineProperty handler returned false for property '"foo"'</span></pre>
    </div>
    <p>上面代码中，<code>defineProperty</code>方法返回<code>false</code>，导致添加新属性会抛出错误。</p>
    <p>注意，如果目标对象不可扩展（extensible），则<code>defineProperty</code>不能增加目标对象上不存在的属性，否则会报错。另外，如果目标对象的某个属性不可写（writable）或不可配置（configurable），则<code>defineProperty</code>方法不得改变这两个设置。
    </p>
    <h3><a href="#getownpropertydescriptor"  class="anchor"
           id="user-content-getownpropertydescriptor">

    </a>getOwnPropertyDescriptor()
    </h3>
    <p><code>getOwnPropertyDescriptor</code>方法拦截<code>Object.getOwnPropertyDescriptor()</code>，返回一个属性描述对象或者<code>undefined</code>。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">getOwnPropertyDescriptor</span> (<span class="pl-smi">target</span>, <span
                class="pl-smi">key</span>) {
    <span class="pl-k">if</span> (key[<span class="pl-c1">0</span>] <span class="pl-k">===</span> <span
                class="pl-s"><span class="pl-pds">'</span>_<span class="pl-pds">'</span></span>) {
      <span class="pl-k">return</span>;
    }
    <span class="pl-k">return</span> <span class="pl-c1">Object</span>.<span
                class="pl-en">getOwnPropertyDescriptor</span>(target, key);
  }
};
<span class="pl-k">var</span> target <span class="pl-k">=</span> { _foo<span class="pl-k">:</span> <span
                class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>, baz<span
                class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>tar<span
                class="pl-pds">'</span></span> };
<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);
<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(proxy, <span class="pl-s"><span
                class="pl-pds">'</span>wat<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> undefined</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(proxy, <span class="pl-s"><span
                class="pl-pds">'</span>_foo<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> undefined</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(proxy, <span class="pl-s"><span
                class="pl-pds">'</span>baz<span class="pl-pds">'</span></span>)
<span class="pl-c"><span
        class="pl-c">//</span> { value: 'tar', writable: true, enumerable: true, configurable: true }</span></pre>
    </div>
    <p>上面代码中，<code>handler.getOwnPropertyDescriptor</code>方法对于第一个字符为下划线的属性名会返回<code>undefined</code>。</p>
    <h3><a href="#getprototypeof"  class="anchor" id="user-content-getprototypeof">

    </a>getPrototypeOf()
    </h3>
    <p><code>getPrototypeOf</code>方法主要用来拦截获取对象原型。具体来说，拦截下面这些操作。</p>
    <ul>
        <li><code>Object.prototype.__proto__</code></li>
        <li><code>Object.prototype.isPrototypeOf()</code></li>
        <li><code>Object.getPrototypeOf()</code></li>
        <li><code>Reflect.getPrototypeOf()</code></li>
        <li><code>instanceof</code></li>
    </ul>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> proto <span class="pl-k">=</span> {};
<span class="pl-k">var</span> p <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>({}, {
  <span class="pl-en">getPrototypeOf</span>(<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> proto;
  }
});
<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(p) <span class="pl-k">===</span> proto <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，<code>getPrototypeOf</code>方法拦截<code>Object.getPrototypeOf()</code>，返回<code>proto</code>对象。</p>
    <p>注意，<code>getPrototypeOf</code>方法的返回值必须是对象或者<code>null</code>，否则报错。另外，如果目标对象不可扩展（extensible），
        <code>getPrototypeOf</code>方法必须返回目标对象的原型对象。</p>
    <h3><a href="#isextensible"  class="anchor" id="user-content-isextensible">

    </a>isExtensible()
    </h3>
    <p><code>isExtensible</code>方法拦截<code>Object.isExtensible</code>操作。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">isExtensible</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">target</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>called<span
                class="pl-pds">"</span></span>);
    <span class="pl-k">return</span> <span class="pl-c1">true</span>;
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">isExtensible</span>(p)
<span class="pl-c"><span class="pl-c">//</span> "called"</span>
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码设置了<code>isExtensible</code>方法，在调用<code>Object.isExtensible</code>时会输出<code>called</code>。</p>
    <p>注意，该方法只能返回布尔值，否则返回值会被自动转为布尔值。</p>
    <p>这个方法有一个强限制，它的返回值必须与目标对象的<code>isExtensible</code>属性保持一致，否则就会抛出错误。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-c1">Object</span>.<span class="pl-en">isExtensible</span>(proxy) <span
                class="pl-k">===</span> <span class="pl-c1">Object</span>.<span class="pl-en">isExtensible</span>(target)</pre>
    </div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">isExtensible</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">target</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">false</span>;
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">isExtensible</span>(p) <span class="pl-c"><span
                class="pl-c">//</span> 报错</span></pre>
    </div>
    <h3><a href="#ownkeys"  class="anchor" id="user-content-ownkeys">

    </a>ownKeys()
    </h3>
    <p><code>ownKeys</code>方法用来拦截对象自身属性的读取操作。具体来说，拦截以下操作。</p>
    <ul>
        <li><code>Object.getOwnPropertyNames()</code></li>
        <li><code>Object.getOwnPropertySymbols()</code></li>
        <li><code>Object.keys()</code></li>
    </ul>
    <p>下面是拦截<code>Object.keys()</code>的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> target <span class="pl-k">=</span> {
  a<span class="pl-k">:</span> <span class="pl-c1">1</span>,
  b<span class="pl-k">:</span> <span class="pl-c1">2</span>,
  c<span class="pl-k">:</span> <span class="pl-c1">3</span>
};

<span class="pl-k">let</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">ownKeys</span>(<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> [<span class="pl-s"><span class="pl-pds">'</span>a<span
                class="pl-pds">'</span></span>];
  }
};

<span class="pl-k">let</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);

<span class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(proxy)
<span class="pl-c"><span class="pl-c">//</span> [ 'a' ]</span></pre>
    </div>
    <p>上面代码拦截了对于<code>target</code>对象的<code>Object.keys()</code>操作，只返回<code>a</code>、<code>b</code>、<code>c</code>三个属性之中的<code>a</code>属性。
    </p>
    <p>下面的例子是拦截第一个字符为下划线的属性名。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> target <span class="pl-k">=</span> {
  _bar<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>,
  _prop<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span
                class="pl-pds">'</span></span>,
  prop<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>baz<span
                class="pl-pds">'</span></span>
};

<span class="pl-k">let</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">ownKeys</span> (<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">Reflect</span>.<span class="pl-en">ownKeys</span>(target).<span
                class="pl-en">filter</span>(<span class="pl-smi">key</span> <span class="pl-k">=&gt;</span> key[<span
                class="pl-c1">0</span>] <span class="pl-k">!==</span> <span class="pl-s"><span
                class="pl-pds">'</span>_<span class="pl-pds">'</span></span>);
  }
};

<span class="pl-k">let</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);
<span class="pl-k">for</span> (<span class="pl-k">let</span> key <span class="pl-k">of</span> <span
                class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(proxy)) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(target[key]);
}
<span class="pl-c"><span class="pl-c">//</span> "baz"</span></pre>
    </div>
    <p>注意，使用<code>Object.keys</code>方法时，有三类属性会被<code>ownKeys</code>方法自动过滤，不会返回。</p>
    <ul>
        <li>目标对象上不存在的属性</li>
        <li>属性名为 Symbol 值</li>
        <li>不可遍历（<code>enumerable</code>）的属性</li>
    </ul>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> target <span class="pl-k">=</span> {
  a<span class="pl-k">:</span> <span class="pl-c1">1</span>,
  b<span class="pl-k">:</span> <span class="pl-c1">2</span>,
  c<span class="pl-k">:</span> <span class="pl-c1">3</span>,
  [<span class="pl-c1">Symbol</span>.<span class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">'</span>secret<span
                class="pl-pds">'</span></span>)]<span class="pl-k">:</span> <span class="pl-s"><span
                class="pl-pds">'</span>4<span class="pl-pds">'</span></span>,
};

<span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(target, <span class="pl-s"><span
                class="pl-pds">'</span>key<span class="pl-pds">'</span></span>, {
  enumerable<span class="pl-k">:</span> <span class="pl-c1">false</span>,
  configurable<span class="pl-k">:</span> <span class="pl-c1">true</span>,
  writable<span class="pl-k">:</span> <span class="pl-c1">true</span>,
  value<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>static<span
                class="pl-pds">'</span></span>
});

<span class="pl-k">let</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">ownKeys</span>(<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> [<span class="pl-s"><span class="pl-pds">'</span>a<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>d<span
                class="pl-pds">'</span></span>, <span class="pl-c1">Symbol</span>.<span class="pl-en">for</span>(<span
                class="pl-s"><span class="pl-pds">'</span>secret<span class="pl-pds">'</span></span>), <span
                class="pl-s"><span class="pl-pds">'</span>key<span class="pl-pds">'</span></span>];
  }
};

<span class="pl-k">let</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);

<span class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(proxy)
<span class="pl-c"><span class="pl-c">//</span> ['a']</span></pre>
    </div>
    <p>上面代码中，<code>ownKeys</code>方法之中，显式返回不存在的属性（<code>d</code>）、Symbol
        值（<code>Symbol.for('secret')</code>）、不可遍历的属性（<code>key</code>），结果都被自动过滤掉。</p>
    <p><code>ownKeys</code>方法还可以拦截<code>Object.getOwnPropertyNames()</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">ownKeys</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> [<span class="pl-s"><span class="pl-pds">'</span>a<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>b<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>c<span
                class="pl-pds">'</span></span>];
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(p)
<span class="pl-c"><span class="pl-c">//</span> [ 'a', 'b', 'c' ]</span></pre>
    </div>
    <p><code>ownKeys</code>方法返回的数组成员，只能是字符串或 Symbol 值。如果有其他类型的值，或者返回的根本不是数组，就会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span class="pl-k">=</span> {};

<span class="pl-k">var</span> p <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(obj, {
  <span class="pl-en">ownKeys</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> [<span class="pl-c1">123</span>, <span class="pl-c1">true</span>, <span
                class="pl-c1">undefined</span>, <span class="pl-c1">null</span>, {}, []];
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(p)
<span class="pl-c"><span class="pl-c">//</span> Uncaught TypeError: 123 is not a valid property name</span></pre>
    </div>
    <p>上面代码中，<code>ownKeys</code>方法虽然返回一个数组，但是每一个数组成员都不是字符串或 Symbol 值，因此就报错了。</p>
    <p>如果目标对象自身包含不可配置的属性，则该属性必须被<code>ownKeys</code>方法返回，否则报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span class="pl-k">=</span> {};
<span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(obj, <span class="pl-s"><span
                class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, {
  configurable<span class="pl-k">:</span> <span class="pl-c1">false</span>,
  enumerable<span class="pl-k">:</span> <span class="pl-c1">true</span>,
  value<span class="pl-k">:</span> <span class="pl-c1">10</span> }
);

<span class="pl-k">var</span> p <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(obj, {
  <span class="pl-en">ownKeys</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> [<span class="pl-s"><span class="pl-pds">'</span>b<span
                class="pl-pds">'</span></span>];
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(p)
<span class="pl-c"><span class="pl-c">//</span> Uncaught TypeError: 'ownKeys' on proxy: trap result did not include 'a'</span></pre>
    </div>
    <p>上面代码中，<code>obj</code>对象的<code>a</code>属性是不可配置的，这时<code>ownKeys</code>方法返回的数组之中，必须包含<code>a</code>，否则会报错。</p>
    <p>另外，如果目标对象是不可扩展的（non-extensition），这时<code>ownKeys</code>方法返回的数组之中，必须包含原对象的所有属性，且不能包含多余的属性，否则报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span class="pl-k">=</span> {
  a<span class="pl-k">:</span> <span class="pl-c1">1</span>
};

<span class="pl-c1">Object</span>.<span class="pl-en">preventExtensions</span>(obj);

<span class="pl-k">var</span> p <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(obj, {
  <span class="pl-en">ownKeys</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>) {
    <span class="pl-k">return</span> [<span class="pl-s"><span class="pl-pds">'</span>a<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>b<span
                class="pl-pds">'</span></span>];
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(p)
<span class="pl-c"><span class="pl-c">//</span> Uncaught TypeError: 'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible</span></pre>
    </div>
    <p>上面代码中，<code>obj</code>对象是不可扩展的，这时<code>ownKeys</code>方法返回的数组之中，包含了<code>obj</code>对象的多余属性<code>b</code>，所以导致了报错。
    </p>
    <h3><a href="#preventextensions"  class="anchor" id="user-content-preventextensions">

    </a>preventExtensions()
    </h3>
    <p><code>preventExtensions</code>方法拦截<code>Object.preventExtensions()</code>。该方法必须返回一个布尔值，否则会被自动转为布尔值。</p>
    <p>
        这个方法有一个限制，只有目标对象不可扩展时（即<code>Object.isExtensible(proxy)</code>为<code>false</code>），<code>proxy.preventExtensions</code>才能返回<code>true</code>，否则会报错。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">preventExtensions</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">target</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">true</span>;
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">preventExtensions</span>(p) <span class="pl-c"><span class="pl-c">//</span> 报错</span></pre>
    </div>
    <p>
        上面代码中，<code>proxy.preventExtensions</code>方法返回<code>true</code>，但这时<code>Object.isExtensible(proxy)</code>会返回<code>true</code>，因此报错。
    </p>
    <p>为了防止出现这个问题，通常要在<code>proxy.preventExtensions</code>方法里面，调用一次<code>Object.preventExtensions</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
  <span class="pl-en">preventExtensions</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span
                class="pl-smi">target</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>called<span
                class="pl-pds">'</span></span>);
    <span class="pl-c1">Object</span>.<span class="pl-en">preventExtensions</span>(target);
    <span class="pl-k">return</span> <span class="pl-c1">true</span>;
  }
});

<span class="pl-c1">Object</span>.<span class="pl-en">preventExtensions</span>(p)
<span class="pl-c"><span class="pl-c">//</span> "called"</span>
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <h3><a href="#setprototypeof"  class="anchor" id="user-content-setprototypeof">

    </a>setPrototypeOf()
    </h3>
    <p><code>setPrototypeOf</code>方法主要用来拦截<code>Object.setPrototypeOf</code>方法。</p>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> handler <span class="pl-k">=</span> {
  <span class="pl-en">setPrototypeOf</span> (<span class="pl-smi">target</span>, <span class="pl-smi">proto</span>) {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>Changing the prototype is forbidden<span
                class="pl-pds">'</span></span>);
  }
};
<span class="pl-k">var</span> proto <span class="pl-k">=</span> {};
<span class="pl-k">var</span> <span class="pl-en">target</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span> () {};
<span class="pl-k">var</span> proxy <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Proxy</span>(target, handler);
<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(proxy, proto);
<span class="pl-c"><span class="pl-c">//</span> Error: Changing the prototype is forbidden</span></pre>
    </div>
    <p>上面代码中，只要修改<code>target</code>的原型对象，就会报错。</p>
    <p>注意，该方法只能返回布尔值，否则会被自动转为布尔值。另外，如果目标对象不可扩展（extensible），<code>setPrototypeOf</code>方法不得改变目标对象的原型。</p>
    <h2><a href="#proxyrevocable"  class="anchor" id="user-content-proxyrevocable">

    </a>Proxy.revocable()
    </h2>
    <p><code>Proxy.revocable</code>方法返回一个可取消的 Proxy 实例。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> target <span class="pl-k">=</span> {};
<span class="pl-k">let</span> handler <span class="pl-k">=</span> {};

<span class="pl-k">let</span> {proxy, revoke} <span class="pl-k">=</span> <span class="pl-c1">Proxy</span>.<span
                class="pl-en">revocable</span>(target, handler);

<span class="pl-smi">proxy</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span
                class="pl-c1">123</span>;
<span class="pl-smi">proxy</span>.<span class="pl-smi">foo</span> <span class="pl-c"><span
                class="pl-c">//</span> 123</span>

<span class="pl-en">revoke</span>();
<span class="pl-smi">proxy</span>.<span class="pl-smi">foo</span> <span class="pl-c"><span class="pl-c">//</span> TypeError: Revoked</span></pre>
    </div>
    <p><code>Proxy.revocable</code>方法返回一个对象，该对象的<code>proxy</code>属性是<code>Proxy</code>实例，<code>revoke</code>属性是一个函数，可以取消<code>Proxy</code>实例。上面代码中，当执行<code>revoke</code>函数之后，再访问<code>Proxy</code>实例，就会抛出一个错误。
    </p>
    <p><code>Proxy.revocable</code>的一个使用场景是，目标对象不允许直接访问，必须通过代理访问，一旦访问结束，就收回代理权，不允许再次访问。</p>
    <h2><a href="#this-问题"  class="anchor" id="user-content-this-问题">

    </a>this 问题
    </h2>
    <p>虽然 Proxy 可以代理针对目标对象的访问，但它不是目标对象的透明代理，即不做任何拦截的情况下，也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下，目标对象内部的<code>this</code>关键字会指向
        Proxy 代理。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">target</span> <span class="pl-k">=</span> {
  <span class="pl-en">m</span><span class="pl-k">:</span> <span class="pl-k">function</span> () {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">this</span> <span
                class="pl-k">===</span> proxy);
  }
};
<span class="pl-k">const</span> <span class="pl-c1">handler</span> <span class="pl-k">=</span> {};

<span class="pl-k">const</span> <span class="pl-c1">proxy</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>(target, handler);

<span class="pl-smi">target</span>.<span class="pl-en">m</span>() <span class="pl-c"><span class="pl-c">//</span> false</span>
<span class="pl-smi">proxy</span>.<span class="pl-en">m</span>()  <span class="pl-c"><span
                class="pl-c">//</span> true</span></pre>
    </div>
    <p>
        上面代码中，一旦<code>proxy</code>代理<code>target.m</code>，后者内部的<code>this</code>就是指向<code>proxy</code>，而不是<code>target</code>。
    </p>
    <p>下面是一个例子，由于<code>this</code>指向的变化，导致 Proxy 无法代理目标对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">_name</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">WeakMap</span>();

<span class="pl-k">class</span> <span class="pl-en">Person</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">name</span>) {
    <span class="pl-smi">_name</span>.<span class="pl-c1">set</span>(<span class="pl-c1">this</span>, name);
  }
  <span class="pl-k">get</span> <span class="pl-en">name</span>() {
    <span class="pl-k">return</span> <span class="pl-smi">_name</span>.<span class="pl-c1">get</span>(<span
                class="pl-c1">this</span>);
  }
}

<span class="pl-k">const</span> <span class="pl-c1">jane</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Person</span>(<span class="pl-s"><span
                class="pl-pds">'</span>Jane<span class="pl-pds">'</span></span>);
<span class="pl-smi">jane</span>.<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> 'Jane'</span>

<span class="pl-k">const</span> <span class="pl-c1">proxy</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>(jane, {});
<span class="pl-smi">proxy</span>.<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> undefined</span></pre>
    </div>
    <p>
        上面代码中，目标对象<code>jane</code>的<code>name</code>属性，实际保存在外部<code>WeakMap</code>对象<code>_name</code>上面，通过<code>this</code>键区分。由于通过<code>proxy.name</code>访问时，<code>this</code>指向<code>proxy</code>，导致无法取到值，所以返回<code>undefined</code>。
    </p>
    <p>此外，有些原生对象的内部属性，只有通过正确的<code>this</code>才能拿到，所以 Proxy 也无法代理这些原生对象的属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">target</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Date</span>();
<span class="pl-k">const</span> <span class="pl-c1">handler</span> <span class="pl-k">=</span> {};
<span class="pl-k">const</span> <span class="pl-c1">proxy</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>(target, handler);

<span class="pl-smi">proxy</span>.<span class="pl-c1">getDate</span>();
<span class="pl-c"><span class="pl-c">//</span> TypeError: this is not a Date object.</span></pre>
    </div>
    <p>上面代码中，<code>getDate</code>方法只能在<code>Date</code>对象实例上面拿到，如果<code>this</code>不是<code>Date</code>对象实例就会报错。这时，<code>this</code>绑定原始对象，就可以解决这个问题。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">target</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Date</span>(<span
            class="pl-s"><span class="pl-pds">'</span>2015-01-01<span class="pl-pds">'</span></span>);
<span class="pl-k">const</span> <span class="pl-c1">handler</span> <span class="pl-k">=</span> {
  <span class="pl-en">get</span>(<span class="pl-smi">target</span>, <span class="pl-smi">prop</span>) {
    <span class="pl-k">if</span> (prop <span class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">'</span>getDate<span
                class="pl-pds">'</span></span>) {
      <span class="pl-k">return</span> <span class="pl-smi">target</span>.<span class="pl-smi">getDate</span>.<span
                class="pl-en">bind</span>(target);
    }
    <span class="pl-k">return</span> <span class="pl-c1">Reflect</span>.<span class="pl-c1">get</span>(target, prop);
  }
};
<span class="pl-k">const</span> <span class="pl-c1">proxy</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Proxy</span>(target, handler);

<span class="pl-smi">proxy</span>.<span class="pl-c1">getDate</span>() <span class="pl-c"><span class="pl-c">//</span> 1</span></pre>
    </div>
    <h2><a href="#实例web-服务的客户端"  class="anchor" id="user-content-实例web-服务的客户端">

    </a>实例：Web 服务的客户端
    </h2>
    <p>Proxy 对象可以拦截目标对象的任意属性，这使得它很合适用来写 Web 服务的客户端。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">service</span> <span class="pl-k">=</span> <span class="pl-en">createWebService</span>(<span
            class="pl-s"><span class="pl-pds">'</span>http://example.com/data<span class="pl-pds">'</span></span>);

<span class="pl-smi">service</span>.<span class="pl-en">employees</span>().<span class="pl-en">then</span>(<span
                class="pl-smi">json</span> <span class="pl-k">=&gt;</span> {
  <span class="pl-k">const</span> <span class="pl-c1">employees</span> <span class="pl-k">=</span> <span class="pl-c1">JSON</span>.<span
                class="pl-c1">parse</span>(json);
  <span class="pl-c"><span class="pl-c">//</span> ···</span>
});</pre>
    </div>
    <p>上面代码新建了一个 Web 服务的接口，这个接口返回各种数据。Proxy 可以拦截这个对象的任意属性，所以不用为每一种数据写一个适配方法，只要写一个 Proxy 拦截就可以了。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">createWebService</span>(<span
            class="pl-smi">baseUrl</span>) {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Proxy</span>({}, {
    <span class="pl-en">get</span>(<span class="pl-smi">target</span>, <span class="pl-smi">propKey</span>, <span
                class="pl-smi">receiver</span>) {
      <span class="pl-k">return</span> () <span class="pl-k">=&gt;</span> <span
                class="pl-en">httpGet</span>(baseUrl<span class="pl-k">+</span><span class="pl-s"><span
                class="pl-pds">'</span>/<span class="pl-pds">'</span></span> <span class="pl-k">+</span> propKey);
    }
  });
}</pre>
    </div>
    <p>同理，Proxy 也可以用来实现数据库的 ORM 层。</p>
</div>
</body>
</html>
